import express from 'express';
import path from 'path';
import { fileURLToPath } from "node:url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

import c2rust from './tools/c2rust.js';
import { chatglm, chatglm_stream } from './tools/chatglm.js';
import { baichuan, baichuan_stream } from './tools/baichuan.js';
import { chatgpt4 } from './tools/chatgpt4.js';
import run from './tools/run.js';
import { genIdFromCppCode } from './utils.js';
import TranspileRecord from './db/TranspileRecord.js';
import auto_transcode from './auto.js';

import db from './db/index.js';

const app = express();

//设置跨域访问
app.all('*', function (req, res, next) {
    // console.log("request accepted");
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Headers', '*');
    res.header('Access-Control-Allow-Methods', '*');
    // res.header('Content-Type', 'application/json;charset=utf-8');
    next();
});

app.use(express.static(path.join(__dirname, 'dist')));
app.use(express.json())

app.get('/hello', (req, res) => {
    res.send('Hello!');
})


/**
 * request
 * {
 *      tool: 'chatglm',
 *      code: '',
 *      prompt: '',
 *      id: '', // 用于标识不同的的对话
 * }
 *
 * response
 * {
 *      status: 0 | 1,
 *      content:
 *      error:
 *      id: ''
 * }
*/

/*
数据库的工作流程
当收到转译请求时，在数据库中，查找是否存在记录，如果有记录且通过校验，则直接返回已经转译的rust代码。 
如果没有记录则进行转译。
等待前端验证通过，则会通过数据库更新接口。将通过校验的代码。存入数据库中。 
*/

// 用于流式返回数据
app.post('/api/transcode/stream', async (req, res) => {

    if (req.body.tool === 'chatglm') {
        await chatglm_stream(req.body, res);
    } else if (req.body.tool === 'baichuan') {
        await baichuan_stream(req.body, res);
    }

    return;


    // console.log('/api/transcode/stream - ', req.body);
    const record_id = await genIdFromCppCode((req.body.code || '').trim());
    // 查找数据库中是否有已经完成的转译记录，有记录，则返回转译结果
    const record = await TranspileRecord.findById(record_id);

    // console.log('record_id:', record_id);
    // console.log('record:', record);

    // 如果没有记录或者记录没有通过校验，则进行转译
    if (!record || !record.is_pass) {
        if (req.body.tool === 'chatglm') {
            await chatglm_stream(req.body, res);
        } else if (req.body.tool === 'baichuan') {
            await baichuan_stream(req.body, res);
        }
    } else {
        // console.log('有记录');
        res.write(JSON.stringify({
            status: 0,
            content: "```rust\n" + record.rust + "\n```",
            // 而record.id根据代码生成的数据库id, 返回前端用于在结果对比通过时更新数据库
            // req.body.id是用于标记多轮次转译对话的id
            id: req.body.id
        }));
        res.end();
        return;
    }
});


/**
 * request
 * {
 *      tool: 'chatglm',
 *      server_tool: '',
 *      exec_params: '',
 *      compile_params: '',
 *      code: '',
 *      prompt: '',
 *      id: '', // 用于标识不同的的对话
 * }
 *
 * response
 * {
 *      status: 0 | 1,
 *      content:
 *      tool: '', // 将最佳结果的工具返回，多次转译选择自动的时候，第二次之后则使用次工具进行多次询问 
 *      error:
 *      id: ''
 * }
 * 
 * 用于自动转译，由于需要多个结果进行对比，所以效率会比较低
 * 工具选择优先级：
 * 0. 数据库已经通过的记录
 * 1. c2rust工具
 * 2. chatgpt4 (暂未接入)
 * 3. 其他大模型
*/
app.post('/api/transcode/auto', async (req, res) => {
    // console.log('/api/transcode/auto', ' - ', req.body);

    // 0. 数据库已经通过的记录 Start ======================
    const record_id = await genIdFromCppCode((req.body.code || '').trim());
    // 查找数据库中是否有已经完成的转译记录，有记录，则返回转译结果
    const record = await TranspileRecord.findById(record_id);

    if (record && record.is_pass) {
        // console.log('有记录');
        console.log('数据库对比通过，直接返回');
        res.write(JSON.stringify({
            status: 0,
            content: record.rust,
            // 而record.id根据代码生成的数据库id, 返回前端用于在结果对比通过时更新数据库
            // req.body.id是用于标记多轮次转译对话的id
            id: req.body.id
        }));
        res.end();
        return;
    }
    // 0. 数据库已经通过的记录 End ======================

    // 编译/执行C代码
    // 如果c代码编译/执行错误，则直接返回错误信息，不进行转译
    let c_stdout = '';
    try {
        c_stdout = await run.c(
            req.body.code,
            req.body.compile_params || '',
            req.body.exec_params || ''
        );
    } catch (e) {
        res.send({
            status: 1,
            error: "C代码编译/执行错误: " + e,
            id: req.body.id
        })
        return;
    }

    // 1. c2rust 优于大模型
    try {
        const content = await c2rust(req.body.code);
        let r_output = await run.rust(content, req.body.exec_params);

        // 成功运行，对比结果
        // 统一C和rust的换行符
        c_stdout = (c_stdout || '').replace(/\r\n/g, "\n").trim();
        r_output = (r_output || '').replace(/\r\n/g, "\n").trim();
        const is_pass = c_stdout === r_output
        await TranspileRecord.createOrUpdate((req.body.code || '').trim(), (content || '').trim(), is_pass);
        if (is_pass) {
            console.log('c2rust转译成功');
            res.send({
                status: 0,
                content,
                tool: 'c2rust',
                id: req.body.id
            })
            return
        }
        console.log('c2rust转译失败，进入大模型转译');
    } catch (e) {
        console.error('[c2rust] 转译/运行输出代码失败:', e);
    }

    // 3. 其他大模型
    await auto_transcode(req, res, req.body.auto_repeat);
})



// 用于更新数据库
/*
request
{
    cpp: 'code',
    rust: 'code',
}
response
{
    status: 0 | 1,
    message: ''
}

*/
app.post('/api/transcode/update', async (req, res) => {
    // console.log('/api/transcode/update', ' - ', req.body);
    try {
        await TranspileRecord.createOrUpdate((req.body.cpp || '').trim(), (req.body.rust || '').trim());
        res.send({
            status: 0,
            message: 'success'
        })
    } catch (e) {
        res.send({
            status: 1,
            message: 'error: ' + e
        })
    }
})

app.get('/api/getNotPassedData', async (req, res) => {
    try {
        const data = await TranspileRecord.findByIsPass(false);
        res.send({
            status: 0,
            data
        })
    } catch (e) {
        res.send({
            status: 1,
            message: 'error: ' + e
        })
    }
})


/**
 * request
 * {
 *      tool: 'c2rust',
 *      code: '',
 *      prompt: '',
 *      id: '', // 用于标识不同的的对话
 * }
 *
 * response
 * {
 *      status: 0 | 1,
 *      code:
 *      error:
 *      id: ''
 * }
*/

app.post('/api/transcode', async (req, res) => {
    // console.log('/api/transcode', ' - ', req.body);

    try {
        let content = '';
        if (req.body.tool === 'c2rust') {
            content = await c2rust(req.body.code);
        } else if (req.body.tool === 'chatglm') {
            content = await chatglm(req.body);
        } else if (req.body.tool === 'chatgpt4') {
            content = await chatgpt4(req.body);
        }
        res.send({
            status: 0,
            content,
            id: req.body.id
        })
    } catch (e) {
        res.send({
            status: 1,
            error: e,
            id: req.body.id
        })
    }
})


/**
 * {
 *      language: 'cpp' | 'rust',
 *      code: ''
 *      compile_params: '',
 *      exec_pramas: ''
 * }
 * {
 *      status: 0 | 1,
 *      output: ''
 * }
 */
app.post('/api/run', async (req, res) => {
    // console.log('/api/run', ' - ', req.body);

    if (req.body.language === 'cpp') {
        try {
            const output = await run.c(
                req.body.code,
                req.body.compile_params || '',
                req.body.exec_params || ''
            );
            res.send({
                status: 0,
                output
            })
        } catch (e) {
            res.send({
                status: 1,
                output: e
            })
        }
    } else if (req.body.language === 'rust') {
        try {
            const output = await run.rust(req.body.code, req.body.exec_params);
            res.send({
                status: 0,
                output
            })
        } catch (e) {
            res.send({
                status: 1,
                output: e
            })
        }
    } else {
        res.send({
            status: 1,
            output: 'Unsupported language.'
        })
    }
})

const MONGODB_URI = 'mongodb://127.0.0.1:27017/c2rust';

db(MONGODB_URI).then(async () => {
    console.log('DB connected:', MONGODB_URI);
}).catch(e => {
    console.log('db error:', e);
})
const port = 24312;
app.listen(port, () => {
    console.log(`Listening on: http://127.0.0.1:${port}`);
});
